/*--------------------------------------------- Noble Steed Engine--------------------------------------*
Authors : Daniel Randle, Alex Bourne
Date Created : April 05 2013

File:
	nsselectionmodel.cpp

Description:
	This file contains the definition for the NSSelectionModel class and any associated functions that help
	the ol' girl out
*-----------------------------------------------------------------------------------------------------*/

#include <nsselectionmodel.h>
#include <nsobjectreference.h>
#include <nsmaparea.h>
#include <nscamera.h>
#include <logfile.h>
#include <nsshader.h>
#include <list>
#include <nsengine.h>
#include <nshextile.h>

#ifdef DEBUG
#include <debug.h>
#endif

/*---------------------------------*
|   class NSSelectionModel BEGINS  |
*----------------------------------*/

NSSelectionModel::NSSelectionModel(NSMapArea * map): 
	mapArea(map),
	selectionPlaceable(true),
	selectionBoxActive(false),
	startingMousePos(NSVec2Df()),
	currentSelection(), 
	selBoxBuffer(0),
	orientation(),
	selBoundingBox(NSBoundingBox::AABB)
{
	selTileShader = new NSShader("TileSelectionShader","selection.fsh","tileselection.vsh","tileselection.gsh");
	selBoxShader = new NSShader("BoxSelectShader","selectionbox.fsh","selectionbox.vsh","selectionbox.gsh");
	selBoxShaderOutline = new NSShader("BoxSelectShaderOutline","selectionbox.fsh","selectionbox.vsh","selectionboxoutline.gsh");
	obbShader = new NSShader("OBBShader","selection.fsh","oboundingbox.vsh");
	aabbShader = new NSShader("AABBShader","selection.fsh","aaboundingbox.vsh");
	setDefaultSelColors();
	_loadSelBox();
}

NSSelectionModel::~NSSelectionModel()
{
	delete selTileShader;
	delete selBoxShader;
	delete selBoxShaderOutline;
	delete obbShader;
	delete aabbShader;
	glDeleteBuffers(1,&selBoxBuffer);
}

void NSSelectionModel::_loadSelBox()
{
	glGenBuffers(1,&selBoxBuffer);
	for (int i = 0; i < SEL_BOX_VERTS; ++i)
		selBoxVerts.push_back(NSVec3Df());
}

void NSSelectionModel::add(NSObjectReference* ref)
{
	if (ref != NULL && !isIn(ref) )
		currentSelection.push_back(ref);
}

void NSSelectionModel::clear()
{
	currentSelection.clear();
}

void NSSelectionModel::checkCollision()
{

}

void NSSelectionModel::del()
{
	while (currentSelection.begin() != currentSelection.end())
	{
		mapArea->removeReference(currentSelection.back());
		currentSelection.pop_back();
	}
}

void NSSelectionModel::draw(const NSMatrix4Df & projCamMat)
{
	drawSelectionLines(projCamMat);
	drawSelectionBox();
}

void NSSelectionModel::drawSelectionLines(const NSMatrix4Df & projCamMat)
{
	selTileShader->enable();
	selTileShader->setUniform("projCamMat",projCamMat);
	selTileShader->setUniform("selColor",NSVec4Df(tileSelectionColor,lineOpacity));
	Iterator iter = begin();
	while (iter != end())
	{
		NSObject * temp = (*iter)->getBase();
		unsigned int ID = (*iter)->getID();
		glLineWidth(lineWidth);
		if (temp->getObjectType() != NSObject::HexTile)
		{
			switch (selBoundingBox)
			{
			case (NSBoundingBox::OBB):
				obbShader->enable();
				obbShader->setUniform("projCamMat",projCamMat);
				obbShader->setUniform("selColor",NSVec4Df(OBBColor,lineOpacity));
				(*iter)->drawOBB();
				break;
			case (NSBoundingBox::AABB):
				aabbShader->enable();
				aabbShader->setUniform("projCamMat",projCamMat);
				aabbShader->setUniform("selColor",NSVec4Df(AABBColor,lineOpacity));
				(*iter)->drawAABB();
				break;
			}

			selTileShader->enable();
			selTileShader->setUniform("projCamMat",projCamMat);
			selTileShader->setUniform("selColor",NSVec4Df(tileSelectionColor,lineOpacity));
		}
		else
			temp->drawSingle(ID);

		glLineWidth(1.0f);
		++iter;
	}
	aabbShader->disable();
}

void NSSelectionModel::drawSelectionBox()
{
	if (selectionBoxActive)
	{
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glBindBuffer(GL_ARRAY_BUFFER, selBoxBuffer);
		glBufferData(GL_ARRAY_BUFFER, sizeof(NSVec3Df) * selBoxVerts.size(), &selBoxVerts[0], GL_DYNAMIC_DRAW);
		glEnableVertexAttribArray(0);
		glBindBuffer(GL_ARRAY_BUFFER, selBoxBuffer);
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
		selBoxShader->enable();
		selBoxShader->setUniform("selColor",NSVec4Df(selBoxColor,fillOpacity));
		glDrawArrays(GL_LINES,0,selBoxVerts.size());
		selBoxShader->disable();
		selBoxShaderOutline->enable();
		selBoxShaderOutline->setUniform("selColor",NSVec4Df(selBoxColor,lineOpacity));
		glDrawArrays(GL_LINES,0,selBoxVerts.size());
		selBoxShaderOutline->disable();
		glDisableVertexAttribArray(0);
		glDisable(GL_BLEND);
	}
}

NSVec3Df NSSelectionModel::getCenter()
{
	if (!isEmpty())
		return currentSelection[0]->getPosition();
	return NSVec3Df();
}

NSObject::ObjectType NSSelectionModel::getCenterObjectType()
{
	if (!isEmpty())
		return currentSelection[0]->getBase()->getObjectType();
	return NSObject::Base;
}

float NSSelectionModel::getDepth(NSCamera * cam)
{
	if (!isEmpty())
		return currentSelection[0]->getDepth(cam);
	return 0.0f;
}

NSVec3Df NSSelectionModel::getOrientation()
{
	if (!isEmpty())
		return currentSelection[0]->getOrientation();
	return NSVec3Df();
}

NSVec2Df NSSelectionModel::getSelBoxStartPos()
{
	return startingMousePos;
}

NSBoundingBox::Type NSSelectionModel::getWhichSelBoundingBox() const
{
	return selBoundingBox;
}

bool NSSelectionModel::isEmpty()
{
	return currentSelection.empty();
}

bool NSSelectionModel::isIn(NSObjectReference * ref)
{
	Iterator iter = begin();
	while (iter != end())
	{
		if (*iter == ref)
			return true;
		++iter;
	}
	return false;
}

bool NSSelectionModel::isSelectionBoxActive()
{
	return selectionBoxActive;
}

void NSSelectionModel::moveXYFromMouse(const NSVec2Df & currentMousePos, const NSVec2Df & lastMousePos)
{
	NSEngine * eng = mapArea->getEngine();
	Iterator iter = begin();
	NSVec3Df amount;
	if (!isEmpty())
	{
		amount = currentSelection[0]->getMoveXYFromMouseNDC(eng->getNDCFromMousePos(currentMousePos),eng->getNDCFromMousePos(lastMousePos), eng->getCamera());
	}
	translate(amount);
}

void NSSelectionModel::moveZFromMouse(const NSVec2Df & currentMousePos, const NSVec2Df & lastMousePos)
{
	NSEngine * eng = mapArea->getEngine();
	Iterator iter = begin();
	NSVec3Df amount;
	if (!isEmpty())
	{
		amount = currentSelection[0]->getMoveZFromMouseNDC(eng->getNDCFromMousePos(currentMousePos),eng->getNDCFromMousePos(lastMousePos), eng->getCamera());
	}
	translate(amount);
}

bool NSSelectionModel::remove(NSObjectReference * ref)
{
	Iterator iter = begin();
	while (iter != end())
	{
		if (*iter == ref)
		{
			currentSelection.erase(iter);
			return true;
		}
		++iter;
	}
	LogFile("ERROR : Tried to remove invalid reference from selection model");
	return false;
}

void NSSelectionModel::rotate( const NSVec3Df & amount)
{
	Iterator iter = begin();
	while (iter != end())
	{
		(*iter)->rotate(amount);
		++iter;
	}
}

void NSSelectionModel::rotate( float xRotate, float yRotate, float zRotate)
{
	rotate(NSVec3Df(xRotate,yRotate,zRotate));
}

void NSSelectionModel::scale(const NSVec3Df & amount)
{
	Iterator iter = begin();
	while (iter != end())
	{
		(*iter)->scale(amount);
		++iter;
	}
}

void NSSelectionModel::scale(float xScale, float yScale, float zScale)
{
	scale(NSVec3Df(xScale,yScale,zScale));
}

void NSSelectionModel::snapAllToGrid()
{
	Iterator iter = begin();
	while (iter != end())
	{
		if ((*iter)->isSnapToGrid())
			(*iter)->snapToGrid();
		++iter;
	}
}

void NSSelectionModel::setSelectionBoxActive(bool set, const NSVec2Df & startingPoint )
{
	selectionBoxActive = set;
	startingMousePos = startingPoint;
	if (!set)
	{
		selBoxVerts.clear();
		selBoxVerts.resize(SEL_BOX_VERTS);
	}
}

void NSSelectionModel::setSelBoxColor(const NSVec3Df & color)
{
	selBoxColor = color;
}

void NSSelectionModel::setTileSelectionColor(const NSVec3Df & color)
{
	tileSelectionColor = color;
}

void NSSelectionModel::setOBBColor(const NSVec3Df & color)
{
	OBBColor = color;
}

void NSSelectionModel::setAABBColor(const NSVec3Df & color)
{
	AABBColor = color;
}

void NSSelectionModel::setDefaultSelColors()
{
	selBoxColor = NSVec3Df(0.98f,0.973f,0.431f);
	tileSelectionColor = selBoxColor;
	OBBColor = NSVec3Df(0.0f,1.0f,0.0f);
	AABBColor = NSVec3Df(1.0f,0.0f,0.0f);
	fillOpacity = 0.2f;
	lineOpacity = 0.7f;
	lineWidth = 1.0f;
}

void NSSelectionModel::setWhichSelBoundingBox(NSBoundingBox::Type box)
{
	selBoundingBox = box;
}

void NSSelectionModel::toggleSelectedGridSnap()
{
	Iterator iter = begin();
	while (iter != end())
	{
		(*iter)->setSnapToGrid(!(*iter)->isSnapToGrid());
		++iter;
	}
}

void NSSelectionModel::translate(const NSVec3Df & amount)
{
	Iterator iter = begin();

	while (iter != end())
	{
		(*iter)->translate(amount);
		++iter;
	}
}

void NSSelectionModel::translate(float xMove, float yMove, float zMove)
{
	translate(NSVec3Df(xMove,yMove,zMove));
}

void NSSelectionModel::updateSelBox(const NSVec2Df & cMousePos)
{
	NSEngine * eng = mapArea->getEngine();
	NSVec2Df ndcMousePos = eng->getNDCFromMousePos(cMousePos);
	NSVec2Df ndcStartMousePos = eng->getNDCFromMousePos(startingMousePos);
	ndcMousePos.y *= -1;
	ndcStartMousePos.y *= -1;

	selBoxVerts.front() = NSVec3Df(ndcStartMousePos.x,ndcStartMousePos.y,1.0);;
	selBoxVerts.back() =  NSVec3Df(ndcMousePos.x,ndcMousePos.y,1.0);
}

NSSelectionModel::Iterator NSSelectionModel::begin()
{
	return currentSelection.begin();
}

NSSelectionModel::Iterator NSSelectionModel::end()
{
	return currentSelection.end();
}